package com.ljx.funchat.chat.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.ljx.funchat.chat.dao.RedbagDao;
import com.ljx.funchat.chat.dao.RedbagRecordDao;
import com.ljx.funchat.chat.domain.entity.Redbag;
import com.ljx.funchat.chat.domain.vo.req.RedbagObtainReq;
import com.ljx.funchat.chat.domain.vo.req.RedbagSendReq;
import com.ljx.funchat.chat.service.IRedbagService;
import com.ljx.funchat.chat.service.adapter.RedbagAdapter;
import com.ljx.funchat.common.constant.RedisKey;
import com.ljx.funchat.common.event.RedbagSendEvent;
import com.ljx.funchat.common.exception.BusinessException;
import com.ljx.funchat.common.service.lock.LockService;
import com.ljx.funchat.common.utils.AssertUtil;
import com.ljx.funchat.common.utils.EncryptUtil;
import com.ljx.funchat.common.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import static com.ljx.funchat.common.constant.RedisKey.*;

@Slf4j
@Service
public class RedbagServiceImpl implements IRedbagService {

    @Autowired
    private RedbagDao redbagDao;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private LockService lockService;

    @Autowired
    private RedbagRecordDao redbagRecordDao;

    @Override
    public void sendRedBag(Long uid, RedbagSendReq req) {
        sendCheck(uid, req);
        // 创建红包
        Redbag redbag = RedbagAdapter.buildRedbagSave(uid, req);
        redbagDao.save(redbag);
        // 发布红包事件
        applicationEventPublisher.publishEvent(new RedbagSendEvent(this, uid, redbag));
    }

    /**
     * 检查红包是否有误
     * @param uid
     * @param req
     */
    private void sendCheck(Long uid, RedbagSendReq req) {
        String code = RedisUtils.getStr(RedisKey.getKey(REDBAG_CODD, uid));
        AssertUtil.isNotEmpty(code, "验证码错误或已过期");
        AssertUtil.equal(code, req.getCode(), "验证码错误或已过期");
        // TODO 检查用户余额是否充足

        // 删除红包验证码缓存
        RedisUtils.del(RedisKey.getKey(REDBAG_CODD, uid));
    }

    @Override
    public String redbagCode(Long uid) {
        // 生成验证码
        String code = RandomUtil.randomNumbers(5);
        RedisUtils.set(RedisKey.getKey(REDBAG_CODD, uid), code, REDBAG_CODD_EXPIRE, TimeUnit.MINUTES);
        // 生成图片
        String pic = createPic(code);

        return pic;
    }

    /**
     * 生成图形验证码
     * @return
     */
    private String createPic(String code) {
        // 生成图片验证码
        ByteArrayOutputStream outputStream = null;
        BufferedImage image = defaultKaptcha.createImage(code);

        outputStream = new ByteArrayOutputStream();
        String imgBase64Encoder = null;
        try {
            // 对字节数组Base64编码
            ImageIO.write(image, "png", outputStream);
            imgBase64Encoder = "data:image/png;base64," + EncryptUtil.encodeBase64(outputStream.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return imgBase64Encoder;
    }

    @Override
    public void obtainRedbag(Long uid, RedbagObtainReq req) {
        Redbag redbag = obtainCheck(uid, req);
        String key = getRedissonKey(uid, req.getRedbagId());
        lockService.executeWithLock(key, REDBAG_LOCK_EXPIRE, TimeUnit.MINUTES, () -> {
            // 检查是否已经抢过了
            Integer count = redbagRecordDao.countByUidAndRedbagId(uid, redbag.getId());
            AssertUtil.equal(count, 0, "您已经抢过该红包了");
            // 随机金额
            Integer randomMoney = redbag.getNumber() == 1 ? redbag.getFinalMoney() :
                    RandomUtil.randomInt(1, redbag.getFinalMoney() - redbag.getFinalNumber() - 1);
            ((IRedbagService) AopContext.currentProxy()).handleObtain(uid, req.getRedbagId(), randomMoney);
        });
    }

    /**
     * 获取分布式锁的key
     * @param uid
     * @param redbagId
     * @return
     */
    private String getRedissonKey(Long uid, Long redbagId) {
        return RedisKey.getKey(RedisKey.REDBAG_LOCK, uid, redbagId);
    }

    /**
     * 检查抢红包
     * @param uid
     * @param req
     */
    private Redbag obtainCheck(Long uid, RedbagObtainReq req) {
        // 判断红包是否有效
        String valid = RedisUtils.getStr(getKey(REDBAG_VALID, req.getRedbagId()));
        AssertUtil.isNotEmpty(valid, "红包已过期");
        // 判断红包是否存在
        Redbag redbag = redbagDao.getById(req.getRedbagId());
        AssertUtil.isNotEmpty(redbag, "红包不存在");
        // 判断数量
        if (redbag.getFinalNumber() < 1){
            throw new BusinessException("红包数量不足");
        }

        return redbag;
    }

    @Override
    @Transactional
    public void handleObtain(Long uid, Long redbagId, Integer randomMoney) {
        // 扣减红包数量
        redbagDao.updateNumberById(redbagId, randomMoney);
        // 保存记录
        redbagRecordDao.save(RedbagAdapter.buildRedbagRecordSave(uid, redbagId, randomMoney));
    }
}
